use std::vec;

use crate::descriptor::field_descriptor_proto::Label;
use swc_common::DUMMY_SP;
use swc_ecma_ast::*;
use swc_ecma_ast::{
    MemberExpr, MemberProp, IfStmt, ReturnStmt, Lit, Accessibility, BinaryOp, Class, ClassMember,
    ClassMethod, Decl, ExportDecl, Expr, MethodKind, ModuleDecl, ModuleItem, Param,
    PropName, Stmt, TsEntityName, TsExprWithTypeArgs, TsType, TsTypeAnn, TsTypeRef, CallExpr,
    ForOfStmt, ForHead, VarDecl, VarDeclKind, VarDeclarator, ComputedPropName, ForInStmt,
    SwitchCase, SwitchStmt, BreakStmt, AssignTarget, AssignTargetPat, BindingIdent, SimpleAssignTarget,
    TsAsExpr, TsKeywordType, TsKeywordTypeKind,
};
use swc_ecma_utils::quote_ident;
use heck::{ToUpperCamelCase, ToLowerCamelCase};
use crate::{
    context::{Context, PropPos},
    descriptor::{
        self, field_descriptor_proto::Type, DescriptorProto, FieldDescriptorProto,
    },
};
use super::{
    common::{normalize_name, normalize_type_name, ns_name},
    LarkPbRuntime,
};

impl LarkPbRuntime {
    pub(super) fn print_verify(
        &self,
        ctx: &mut Context,
        descriptor: &DescriptorProto,
        ns: &mut Vec<String>,
    ) -> ModuleItem {
        let verifier = self.get_lark_import(ctx, Self::LARK_VERIFIER_NAME);
        let interface_type = ctx.lazy_decl_type_ref(&ns_name(ns, descriptor.name()), PropPos::Interface);

        let implement: TsExprWithTypeArgs = TsExprWithTypeArgs {
            span: DUMMY_SP,
            expr: Box::new(Expr::Ident(verifier.clone())),
            type_args: Some(Box::new(swc_ecma_ast::TsTypeParamInstantiation {
                span: DUMMY_SP,
                params: vec![Box::new(TsType::TsTypeRef(TsTypeRef {
                    span: DUMMY_SP,
                    type_name: TsEntityName::Ident(interface_type.clone()),
                    type_params: None,
                }))],
            })),
        };

        let stmts = self.verify_setup_inner(ctx, descriptor, ns);

        let verify_method = ClassMethod {
            span: DUMMY_SP,
            key: PropName::Ident(quote_ident!("verify")),
            function: Box::new(swc_ecma_ast::Function {
                params: vec![Param {
                    span: DUMMY_SP,
                    decorators: vec![],
                    pat: Pat::Ident(BindingIdent {
                        id: quote_ident!("message"),
                        type_ann: Some(Box::new(TsTypeAnn {
                            span: DUMMY_SP,
                            type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                span: DUMMY_SP,
                                type_name: TsEntityName::Ident(interface_type.clone()),
                                type_params: None,
                            })),
                        })),
                    }),
                }],
                decorators: vec![],
                span: DUMMY_SP,
                body: Some(swc_ecma_ast::BlockStmt {
                    span: DUMMY_SP,
                    stmts,
                }),
                is_generator: false,
                is_async: false,
                type_params: None,
                return_type: Some(Box::new(TsTypeAnn {
                    span: DUMMY_SP,
                    type_ann: Box::new(TsType::TsUnionOrIntersectionType(
                        swc_ecma_ast::TsUnionOrIntersectionType::TsUnionType(
                            swc_ecma_ast::TsUnionType {
                                span: DUMMY_SP,
                                types: vec![
                                    Box::new(TsType::TsKeywordType(
                                        swc_ecma_ast::TsKeywordType {
                                            span: DUMMY_SP,
                                            kind: swc_ecma_ast::TsKeywordTypeKind::TsStringKeyword,
                                        },
                                    )),
                                    Box::new(TsType::TsKeywordType(
                                        swc_ecma_ast::TsKeywordType {
                                            span: DUMMY_SP,
                                            kind: swc_ecma_ast::TsKeywordTypeKind::TsUndefinedKeyword,
                                        },
                                    )),
                                ],
                            },
                        ),
                    )),
                })),
            }),
            kind: MethodKind::Method,
            is_static: false,
            accessibility: Some(Accessibility::Public),
            is_abstract: false,
            is_optional: false,
            is_override: false,
        };

        let class = Class {
            span: DUMMY_SP,
            decorators: vec![],
            body: vec![ClassMember::Method(verify_method)],
            super_class: None,
            is_abstract: false,
            type_params: None,
            super_type_params: None,
            implements: vec![implement],
        };

        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
            span: DUMMY_SP,
            decl: Decl::Class(swc_ecma_ast::ClassDecl {
                ident: quote_ident!("$Verifier"),
                declare: false,
                class: Box::new(class),
            }),
        }))
    }

    pub fn verify_setup_inner(
        &self,
        ctx: &mut Context,
        descriptor: &DescriptorProto,
        ns: &mut Vec<String>,
    ) -> Vec<Stmt> {
        let mut stmts = vec![];

        stmts.push(Stmt::If(IfStmt {
            span: DUMMY_SP,
            test: Box::new(Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::LogicalOr,
                left: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: Box::new(Expr::Ident(quote_ident!("message"))),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("object".into()))),
                })),
                right: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::EqEqEq,
                    left: Box::new(Expr::Ident(quote_ident!("message"))),
                    right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
                })),
            })),
            cons: Box::new(Stmt::Block(BlockStmt {
                span: DUMMY_SP,
                stmts: vec![Stmt::Return(self.create_return_stmt("object expected".to_string()))],
            })),
            alt: None,
        }));

        if !descriptor.oneof_decl.is_empty() {
            stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
                span: DUMMY_SP,
                kind: VarDeclKind::Let,
                declare: false,
                decls: vec![VarDeclarator {
                    span: DUMMY_SP,
                    name: Pat::Ident(quote_ident!("properties").into()),
                    init: Some(Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::Minus,
                        arg: Box::new(Expr::Lit(Lit::Num(Number {
                            span: DUMMY_SP,
                            value: 1.0,
                            raw: None,
                        }))),
                    }))),
                    definite: false,
                }],
            }))));
        }

        for field in descriptor.field.iter().filter(|f| !f.has_oneof_index()) {
            stmts.push(self.check_field_type(ctx, field));
        }

        for field in descriptor.field.iter().filter(|f| f.has_oneof_index()) {
            stmts.push(self.check_oneof_field_type(ctx, descriptor, field));
        }

        stmts.push(Stmt::Return(ReturnStmt {
            span: DUMMY_SP,
            arg: Some(Box::new(Expr::Ident(quote_ident!("undefined")))),
        }));

        stmts
    }

    fn check_field_type(&self, ctx: &mut Context, field: &FieldDescriptorProto) -> Stmt {
        let field_name = normalize_name(field.name());
        let field_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("message"))),
            prop: MemberProp::Ident(quote_ident!(&*field_name)),
        }));

        let inner_check = if field.is_map(ctx) {
            self.build_map_check(ctx, field)
        } else if field.label() == Label::LABEL_REPEATED {
            self.build_repeated_check(ctx, field)
        } else if field.type_() == Type::TYPE_ENUM {
            self.build_enum_check(ctx, field)
        } else {
            self.build_scalar_check(field, &field_name)
        };

        Stmt::If(IfStmt {
            span: DUMMY_SP,
            test: Box::new(Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::NotEq,
                left: field_access,
                right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
            })),
            cons: Box::new(match inner_check {
                // If inner_check is already a block, use it directly to avoid double braces
                Stmt::Block(_) => inner_check,
                // Otherwise, wrap it in a block
                _ => Stmt::Block(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![inner_check],
                }),
            }),
            alt: None,
        })
    }

    fn build_repeated_check(&self, ctx: &mut Context, field: &FieldDescriptorProto) -> Stmt {
        let field_name = normalize_name(field.name());
        let field_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("message"))),
            prop: MemberProp::Ident(quote_ident!(&*field_name)),
        }));

        // Determine the TypeScript type for the array type annotation
        let (element_ts_type, is_message_type) = match field.type_() {
            Type::TYPE_STRING => (TsKeywordTypeKind::TsStringKeyword, false),
            Type::TYPE_BOOL => (TsKeywordTypeKind::TsBooleanKeyword, false),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => (TsKeywordTypeKind::TsBigIntKeyword, false),
            Type::TYPE_MESSAGE => (TsKeywordTypeKind::TsObjectKeyword, true), // For message types, we'll handle this specially
            _ => (TsKeywordTypeKind::TsNumberKeyword, false), // For all numeric types and others
        };

        // Create error message for element validation
        let element_error_msg = match field.type_() {
            Type::TYPE_STRING => format!("{} element: string expected", field_name),
            Type::TYPE_BOOL => format!("{} element: boolean expected", field_name),
            Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => 
                format!("{} element: integer expected", field_name),
            Type::TYPE_UINT32 | Type::TYPE_FIXED32 => 
                format!("{} element: non-negative integer expected", field_name),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => format!("{} element: BigInt expected", field_name),
            Type::TYPE_DOUBLE | Type::TYPE_FLOAT => 
                format!("{} element: number expected", field_name),
            Type::TYPE_BYTES => format!("{} element: Uint8Array expected", field_name),
            Type::TYPE_MESSAGE => {
                let type_name = field.type_name();
                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                format!("{} element: {} expected", field_name, raw_type_name)
            },
            _ => format!("{} element: expected", field_name),
        };

        // Create the element validation check
        let element_check = match field.type_() {
            Type::TYPE_STRING => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(Expr::Ident(quote_ident!("i"))),
                            }),
                        })),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("string".into()))),
                })),
                cons: Box::new(self.create_return_block(element_error_msg)),
                alt: None,
            }),
            Type::TYPE_BOOL => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(Expr::Ident(quote_ident!("i"))),
                            }),
                        })),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("boolean".into()))),
                })),
                cons: Box::new(self.create_return_block(element_error_msg)),
                alt: None,
            }),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(Expr::Ident(quote_ident!("i"))),
                            }),
                        })),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("bigint".into()))),
                })),
                cons: Box::new(self.create_return_block(element_error_msg)),
                alt: None,
            }),
            Type::TYPE_BYTES => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("Uint8Array"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(element_error_msg)),
                alt: None,
            }),
            Type::TYPE_MESSAGE => {
                // For message types, call the corresponding verifier
                let type_name = field.type_name();
                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                
                // Create a variable declaration for the verification result
                let verify_call = Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(quote_ident!("error").into()),
                        init: Some(Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Member(MemberExpr {
                                        span: DUMMY_SP,
                                        obj: Box::new(Expr::Ident(quote_ident!(raw_type_name))),
                                        prop: MemberProp::Ident(quote_ident!("$meta")),
                                    })),
                                    prop: MemberProp::Ident(quote_ident!("verify")),
                                })),
                                prop: MemberProp::Ident(quote_ident!("verify")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                                    prop: MemberProp::Computed(ComputedPropName {
                                        span: DUMMY_SP,
                                        expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                    }),
                                })),
                            }],
                            type_args: None,
                        }))),
                        definite: false,
                    }],
                })));

                // Check if error is not undefined and return it
                let error_check = Stmt::If(IfStmt {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEqEq,
                        left: Box::new(Expr::Ident(quote_ident!("error"))),
                        right: Box::new(Expr::Ident(quote_ident!("undefined"))),
                    })),
                    cons: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![Stmt::Return(ReturnStmt {
                            span: DUMMY_SP,
                            arg: Some(Box::new(Expr::Ident(quote_ident!("error")))),
                        })],
                    })),
                    alt: None,
                });

                Stmt::Block(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![verify_call, error_check],
                })
            },
            _ => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(Expr::Ident(quote_ident!("i"))),
                            }),
                        })),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("number".into()))),
                })),
                cons: Box::new(self.create_return_block(element_error_msg)),
                alt: None,
            }),
        };

        Stmt::Block(BlockStmt {
            span: DUMMY_SP,
            stmts: vec![
                // Array check: if (!Array.isArray(message.fieldName))
                Stmt::If(IfStmt {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::Bang,
                        arg: Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("Array"))),
                                prop: MemberProp::Ident(quote_ident!("isArray")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: field_access.clone(),
                            }],
                            type_args: None,
                        })),
                    })),
                    cons: Box::new(self.create_return_block(format!("{}: array expected", field_name))),
                    alt: None,
                }),
                // Type cast: let fieldName = message.fieldName as ElementType[];
                Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(quote_ident!(&*field_name).into()),
                        init: Some(Box::new(Expr::TsAs(TsAsExpr {
                            span: DUMMY_SP,
                            expr: field_access,
                            type_ann: Box::new(if is_message_type {
                                // For message types, use Array<IMessageType> instead of object[]
                                let type_name = field.type_name();
                                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                                let interface_name = format!("I{}", raw_type_name);
                                
                                TsType::TsTypeRef(swc_ecma_ast::TsTypeRef {
                                    span: DUMMY_SP,
                                    type_name: swc_ecma_ast::TsEntityName::Ident(quote_ident!("Array")),
                                    type_params: Some(Box::new(swc_ecma_ast::TsTypeParamInstantiation {
                                        span: DUMMY_SP,
                                        params: vec![Box::new(TsType::TsTypeRef(swc_ecma_ast::TsTypeRef {
                                            span: DUMMY_SP,
                                            type_name: swc_ecma_ast::TsEntityName::Ident(quote_ident!(&*interface_name)),
                                            type_params: None,
                                        }))],
                                    })),
                                })
                            } else {
                                // For primitive types, use the original array syntax
                                TsType::TsArrayType(swc_ecma_ast::TsArrayType {
                                    span: DUMMY_SP,
                                    elem_type: Box::new(TsType::TsKeywordType(TsKeywordType {
                                        span: DUMMY_SP,
                                        kind: element_ts_type,
                                    })),
                                })
                            }),
                        }))),
                        definite: false,
                    }],
                }))),
                // For loop: for (let i = 0; i < fieldName.length; i++)
                Stmt::For(ForStmt {
                    span: DUMMY_SP,
                    init: Some(VarDeclOrExpr::VarDecl(Box::new(VarDecl {
                        span: DUMMY_SP,
                        kind: VarDeclKind::Let,
                        declare: false,
                        decls: vec![VarDeclarator {
                            span: DUMMY_SP,
                            name: Pat::Ident(quote_ident!("i").into()),
                            init: Some(Box::new(Expr::Lit(Lit::Num(Number {
                                span: DUMMY_SP,
                                value: 0.0,
                                raw: None,
                            })))),
                            definite: false,
                        }],
                    }))),
                    test: Some(Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::Lt,
                        left: Box::new(Expr::Ident(quote_ident!("i"))),
                        right: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Ident(quote_ident!("length")),
                        })),
                    }))),
                    update: Some(Box::new(Expr::Update(UpdateExpr {
                        span: DUMMY_SP,
                        op: UpdateOp::PlusPlus,
                        prefix: false,
                        arg: Box::new(Expr::Ident(quote_ident!("i"))),
                    }))),
                    body: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![element_check],
                    })),
                }),
            ],
        })
    }

    fn build_map_check(&self, ctx: &mut Context, field: &FieldDescriptorProto) -> Stmt {
        let field_name = normalize_name(field.name());
        // NOTE: Ensure ctx has a method to get map entry details.
        let map_entry_descriptor = ctx.get_map_entry(field.type_name()).unwrap();
        let value_field = &map_entry_descriptor.field[1];

        let field_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("message"))),
            prop: MemberProp::Ident(quote_ident!(&*field_name)),
        }));

        // Determine the TypeScript type for the Record type annotation
        let (value_ts_type, is_value_message_type) = match value_field.type_() {
            Type::TYPE_STRING => (TsKeywordTypeKind::TsStringKeyword, false),
            Type::TYPE_BOOL => (TsKeywordTypeKind::TsBooleanKeyword, false),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => (TsKeywordTypeKind::TsBigIntKeyword, false),
            Type::TYPE_MESSAGE => (TsKeywordTypeKind::TsObjectKeyword, true), // For message types, we'll handle this specially
            _ => (TsKeywordTypeKind::TsNumberKeyword, false), // For all numeric types and others
        };

        // Create error message for map value validation
        let value_error_msg = match value_field.type_() {
            Type::TYPE_STRING => format!("{} map value error: string expected", field_name),
            Type::TYPE_BOOL => format!("{} map value error: boolean expected", field_name),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => format!("{} map value error: BigInt expected", field_name),
            Type::TYPE_BYTES => format!("{} map value error: Uint8Array expected", field_name),
            Type::TYPE_MESSAGE => {
                let type_name = value_field.type_name();
                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                format!("{} map value error: {} expected", field_name, raw_type_name)
            },
            _ => format!("{} map value error: integer expected", field_name),
        };

        // Create the value validation check based on type using instanceof
        let value_check = match value_field.type_() {
            Type::TYPE_STRING => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("string"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(value_error_msg)),
                alt: None,
            }),
            Type::TYPE_BOOL => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("boolean"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(value_error_msg)),
                alt: None,
            }),
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | 
            Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("bigint"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(value_error_msg)),
                alt: None,
            }),
            Type::TYPE_BYTES => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("Uint8Array"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(value_error_msg)),
                alt: None,
            }),
            Type::TYPE_MESSAGE => {
                // For message types, call the corresponding verifier
                let type_name = value_field.type_name();
                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                
                // Create a variable declaration for the verification result
                let verify_call = Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(quote_ident!("error").into()),
                        init: Some(Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Member(MemberExpr {
                                        span: DUMMY_SP,
                                        obj: Box::new(Expr::Ident(quote_ident!(raw_type_name))),
                                        prop: MemberProp::Ident(quote_ident!("$meta")),
                                    })),
                                    prop: MemberProp::Ident(quote_ident!("verify")),
                                })),
                                prop: MemberProp::Ident(quote_ident!("verify")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                    prop: MemberProp::Computed(ComputedPropName {
                                        span: DUMMY_SP,
                                        expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                    }),
                                })),
                            }],
                            type_args: None,
                        }))),
                        definite: false,
                    }],
                })));

                // Check if error is not undefined and return it
                let error_check = Stmt::If(IfStmt {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEqEq,
                        left: Box::new(Expr::Ident(quote_ident!("error"))),
                        right: Box::new(Expr::Ident(quote_ident!("undefined"))),
                    })),
                    cons: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![Stmt::Return(ReturnStmt {
                            span: DUMMY_SP,
                            arg: Some(Box::new(Expr::Ident(quote_ident!("error")))),
                        })],
                    })),
                    alt: None,
                });

                Stmt::Block(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![verify_call, error_check],
                })
            },
            _ => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(Expr::Ident(quote_ident!("i"))),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("number"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(value_error_msg)),
                alt: None,
            }),
        };

        Stmt::Block(BlockStmt {
            span: DUMMY_SP,
            stmts: vec![
                // Object type check: typeof !== "object" || === null || Array.isArray()
                Stmt::If(IfStmt {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::LogicalOr,
                        left: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::LogicalOr,
                            left: Box::new(Expr::Bin(BinExpr {
                                span: DUMMY_SP,
                                op: BinaryOp::NotEqEq,
                                left: Box::new(Expr::Unary(UnaryExpr {
                                    span: DUMMY_SP,
                                    op: UnaryOp::TypeOf,
                                    arg: field_access.clone(),
                                })),
                                right: Box::new(Expr::Lit(Lit::Str("object".into()))),
                            })),
                            right: Box::new(Expr::Bin(BinExpr {
                                span: DUMMY_SP,
                                op: BinaryOp::EqEqEq,
                                left: field_access.clone(),
                                right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
                            })),
                        })),
                        right: Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("Array"))),
                                prop: MemberProp::Ident(quote_ident!("isArray")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: field_access.clone(),
                            }],
                            type_args: None,
                        })),
                    })),
                    cons: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![Stmt::Return(self.create_return_stmt(format!("{}: object expected", field_name)))],
                    })),
                    alt: None,
                }),
                // Type conversion: let fieldName = field as Record<string, ValueType>
                Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(quote_ident!(&*field_name).into()),
                        init: Some(Box::new(Expr::TsAs(TsAsExpr {
                            span: DUMMY_SP,
                            expr: field_access,
                            type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                span: DUMMY_SP,
                                type_name: TsEntityName::Ident(quote_ident!("Record")),
                                type_params: Some(Box::new(swc_ecma_ast::TsTypeParamInstantiation {
                                    span: DUMMY_SP,
                                    params: vec![
                                        Box::new(TsType::TsKeywordType(TsKeywordType {
                                            span: DUMMY_SP,
                                            kind: TsKeywordTypeKind::TsStringKeyword,
                                        })),
                                        Box::new(if is_value_message_type {
                                            // For message types, use IMessageType
                                            let type_name = value_field.type_name();
                                            let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                                            let interface_name = format!("I{}", raw_type_name);
                                            
                                            TsType::TsTypeRef(swc_ecma_ast::TsTypeRef {
                                                span: DUMMY_SP,
                                                type_name: swc_ecma_ast::TsEntityName::Ident(quote_ident!(&*interface_name)),
                                                type_params: None,
                                            })
                                        } else {
                                            // For primitive types, use keyword type
                                            TsType::TsKeywordType(TsKeywordType {
                                                span: DUMMY_SP,
                                                kind: value_ts_type,
                                            })
                                        }),
                                    ],
                                })),
                            })),
                        }))),
                        definite: false,
                    }],
                }))),
                // Create values array with type annotation: let values: ValueType[] = []
                Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(BindingIdent {
                            id: quote_ident!("values"),
                            type_ann: Some(Box::new(TsTypeAnn {
                                span: DUMMY_SP,
                                type_ann: Box::new(if is_value_message_type {
                                    // For message types, use Array<IMessageType>
                                    let type_name = value_field.type_name();
                                    let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                                    let interface_name = format!("I{}", raw_type_name);
                                    
                                    TsType::TsTypeRef(swc_ecma_ast::TsTypeRef {
                                        span: DUMMY_SP,
                                        type_name: swc_ecma_ast::TsEntityName::Ident(quote_ident!("Array")),
                                        type_params: Some(Box::new(swc_ecma_ast::TsTypeParamInstantiation {
                                            span: DUMMY_SP,
                                            params: vec![Box::new(TsType::TsTypeRef(swc_ecma_ast::TsTypeRef {
                                                span: DUMMY_SP,
                                                type_name: swc_ecma_ast::TsEntityName::Ident(quote_ident!(&*interface_name)),
                                                type_params: None,
                                            }))],
                                        })),
                                    })
                                } else {
                                    // For primitive types, use the original array syntax
                                    TsType::TsArrayType(swc_ecma_ast::TsArrayType {
                                        span: DUMMY_SP,
                                        elem_type: Box::new(TsType::TsKeywordType(TsKeywordType {
                                            span: DUMMY_SP,
                                            kind: value_ts_type,
                                        })),
                                    })
                                }),
                            })),
                        }),
                        init: Some(Box::new(Expr::Array(ArrayLit {
                            span: DUMMY_SP,
                            elems: vec![],
                        }))),
                        definite: false,
                    }],
                }))),
                // Use fieldName.forEach to collect values: fieldName.forEach((value, key) => { values.push(value) })
                Stmt::Expr(ExprStmt {
                    span: DUMMY_SP,
                    expr: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                            prop: MemberProp::Ident(quote_ident!("forEach")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(Expr::Arrow(ArrowExpr {
                                span: DUMMY_SP,
                                params: vec![
                                    Pat::Ident(quote_ident!("value").into()),
                                    Pat::Ident(quote_ident!("key").into()),
                                ],
                                body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
                                    span: DUMMY_SP,
                                    stmts: vec![Stmt::Expr(ExprStmt {
                                        span: DUMMY_SP,
                                        expr: Box::new(Expr::Call(CallExpr {
                                            span: DUMMY_SP,
                                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                                span: DUMMY_SP,
                                                obj: Box::new(Expr::Ident(quote_ident!("values"))),
                                                prop: MemberProp::Ident(quote_ident!("push")),
                                            }))),
                                            args: vec![ExprOrSpread {
                                                spread: None,
                                                expr: Box::new(Expr::Ident(quote_ident!("value"))),
                                            }],
                                            type_args: None,
                                        })),
                                    })],
                                })),
                                is_async: false,
                                is_generator: false,
                                type_params: None,
                                return_type: None,
                            })),
                        }],
                        type_args: None,
                    })),
                }),
                // For loop to validate each value: for (let i = 0; i < values.length; i++)
                Stmt::For(ForStmt {
                    span: DUMMY_SP,
                    init: Some(VarDeclOrExpr::VarDecl(Box::new(VarDecl {
                        span: DUMMY_SP,
                        kind: VarDeclKind::Let,
                        declare: false,
                        decls: vec![VarDeclarator {
                            span: DUMMY_SP,
                            name: Pat::Ident(quote_ident!("i").into()),
                            init: Some(Box::new(Expr::Lit(Lit::Num(Number {
                                span: DUMMY_SP,
                                value: 0.0,
                                raw: None,
                            })))),
                            definite: false,
                        }],
                    }))),
                    test: Some(Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::Lt,
                        left: Box::new(Expr::Ident(quote_ident!("i"))),
                        right: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("values"))),
                            prop: MemberProp::Ident(quote_ident!("length")),
                        })),
                    }))),
                    update: Some(Box::new(Expr::Update(UpdateExpr {
                        span: DUMMY_SP,
                        op: UpdateOp::PlusPlus,
                        prefix: false,
                        arg: Box::new(Expr::Ident(quote_ident!("i"))),
                    }))),
                    body: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![value_check],
                    })),
                }),
            ],
        })
    }

    fn build_enum_check(&self, ctx: &mut Context, field: &FieldDescriptorProto) -> Stmt {
        let field_name = normalize_name(field.name());
        let field_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("message"))),
            prop: MemberProp::Ident(quote_ident!(&*field_name)),
        }));

        // NOTE: Ensure ctx has a method to get enum details.
        let enum_descriptor = ctx.get_enum(field.type_name()).unwrap();
        let mut cases: Vec<SwitchCase> = enum_descriptor
            .value
            .iter()
            .map(|v| SwitchCase {
                span: DUMMY_SP,
                test: Some(Box::new(Expr::Lit(Lit::Num(Number {
                    span: DUMMY_SP,
                    value: v.number() as f64,
                    raw: None,
                })))),
                cons: vec![],
            })
            .collect();

        // Ensure the last valid case has a break statement
        if !cases.is_empty() {
            cases.last_mut().unwrap().cons.push(Stmt::Break(BreakStmt {
                span: DUMMY_SP,
                label: None,
            }));
        }
        
        // Add default case with proper error handling
        cases.push(SwitchCase {
            span: DUMMY_SP,
            test: None, // Default case
            cons: vec![Stmt::Return(self.create_return_stmt(format!(
                "{}: enum value expected",
                field_name
            )))],
        });

        // Implement the correct condition check: typeof field !== "number" && message.hasOwnProperty('field')
        Stmt::If(IfStmt {
            span: DUMMY_SP,
            test: Box::new(Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::LogicalAnd,
                left: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(Expr::Unary(UnaryExpr {
                        span: DUMMY_SP,
                        op: UnaryOp::TypeOf,
                        arg: field_access.clone(),
                    })),
                    right: Box::new(Expr::Lit(Lit::Str("number".into()))),
                })),
                right: Box::new(Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(quote_ident!("message"))),
                        prop: MemberProp::Ident(quote_ident!("hasOwnProperty")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(Expr::Lit(Lit::Str(field_name.clone().into()))),
                    }],
                    type_args: None,
                })),
            })),
            cons: Box::new(Stmt::Block(BlockStmt {
                span: DUMMY_SP,
                stmts: vec![
                    // Add type conversion as int for enum variable declaration
                    Stmt::Decl(Decl::Var(Box::new(VarDecl {
                        span: DUMMY_SP,
                        kind: VarDeclKind::Let,
                        declare: false,
                        decls: vec![VarDeclarator {
                            span: DUMMY_SP,
                            name: Pat::Ident(quote_ident!(&*field_name).into()),
                            init: Some(Box::new(Expr::TsAs(TsAsExpr {
                                span: DUMMY_SP,
                                expr: field_access,
                                type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                    span: DUMMY_SP,
                                    type_name: TsEntityName::Ident(quote_ident!("int")),
                                    type_params: None,
                                })),
                            }))),
                            definite: false,
                        }],
                    }))),
                    Stmt::Switch(SwitchStmt {
                        span: DUMMY_SP,
                        discriminant: Box::new(Expr::Ident(quote_ident!(&*field_name))),
                        cases,
                    }),
                ],
            })),
            alt: None,
        })
    }

    fn build_scalar_check(&self, field: &FieldDescriptorProto, error_prefix: &str) -> Stmt {
        let field_name = normalize_name(field.name());
        let field_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("message"))),
            prop: MemberProp::Ident(quote_ident!(&*field_name)),
        }));
        self.build_scalar_check_for_expr(field, field_access, error_prefix)
    }

    fn build_scalar_check_for_variable(
        &self,
        field: &FieldDescriptorProto,
        variable_name: &str,
        error_prefix: &str,
    ) -> Stmt {
        let var_access = Box::new(Expr::Ident(quote_ident!(variable_name)));
        self.build_scalar_check_for_expr(field, var_access, error_prefix)
    }

    fn build_scalar_check_for_expr(
        &self,
        field: &FieldDescriptorProto,
        field_access: Box<Expr>,
        error_prefix: &str,
    ) -> Stmt {
        match field.type_() {
            Type::TYPE_DOUBLE | Type::TYPE_FLOAT => Stmt::If(self.create_typeof_check(
                field_access,
                "number",
                format!("{}: number expected", error_prefix),
            )),
            Type::TYPE_INT32 | Type::TYPE_SFIXED32 | Type::TYPE_SINT32 => Stmt::If(
                self.create_typeof_check(
                    field_access,
                    "number",
                    format!("{}: integer expected", error_prefix),
                ),
            ),
            Type::TYPE_UINT32 | Type::TYPE_FIXED32 => Stmt::If(self.create_typeof_check(
                field_access,
                "number",
                format!("{}: non-negative integer expected", error_prefix),
            )),
            Type::TYPE_INT64 | Type::TYPE_SFIXED64 | Type::TYPE_SINT64 => Stmt::If(
                self.create_typeof_check(
                    field_access,
                    "bigint",
                    format!("{}: BigInt expected", error_prefix),
                ),
            ),
            Type::TYPE_UINT64 | Type::TYPE_FIXED64 => Stmt::If(self.create_typeof_check(
                field_access,
                "bigint",
                format!("{}: non-negative BigInt expected", error_prefix),
            )),
            Type::TYPE_BOOL => Stmt::If(self.create_typeof_check(
                field_access,
                "boolean",
                format!("{}: boolean expected", error_prefix),
            )),
            Type::TYPE_STRING => Stmt::If(self.create_typeof_check(
                field_access,
                "string",
                format!("{}: string expected", error_prefix),
            )),
            Type::TYPE_BYTES => Stmt::If(IfStmt {
                span: DUMMY_SP,
                test: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::Bang,
                    arg: Box::new(Expr::Paren(ParenExpr {
                        span: DUMMY_SP,
                        expr: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::InstanceOf,
                            left: field_access.clone(),
                            right: Box::new(Expr::Ident(quote_ident!("Uint8Array"))),
                        })),
                    })),
                })),
                cons: Box::new(self.create_return_block(format!(
                    "{}: Uint8Array expected",
                    error_prefix
                ))),
                alt: None,
            }),
            Type::TYPE_MESSAGE => {
                // For message types, call the corresponding verifier
                let type_name = field.type_name();
                let raw_type_name = &type_name[type_name.rfind('.').map(|i| i + 1).unwrap_or(0)..];
                
                // Create a variable declaration for the verification result
                let verify_call = Stmt::Decl(Decl::Var(Box::new(VarDecl {
                    span: DUMMY_SP,
                    kind: VarDeclKind::Let,
                    declare: false,
                    decls: vec![VarDeclarator {
                        span: DUMMY_SP,
                        name: Pat::Ident(quote_ident!("error").into()),
                        init: Some(Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Member(MemberExpr {
                                        span: DUMMY_SP,
                                        obj: Box::new(Expr::Ident(quote_ident!(raw_type_name))),
                                        prop: MemberProp::Ident(quote_ident!("$meta")),
                                    })),
                                    prop: MemberProp::Ident(quote_ident!("verify")),
                                })),
                                prop: MemberProp::Ident(quote_ident!("verify")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: field_access,
                            }],
                            type_args: None,
                        }))),
                        definite: false,
                    }],
                })));

                // Check if error is not undefined and return it
                let error_check = Stmt::If(IfStmt {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEqEq,
                        left: Box::new(Expr::Ident(quote_ident!("error"))),
                        right: Box::new(Expr::Ident(quote_ident!("undefined"))),
                    })),
                    cons: Box::new(Stmt::Block(BlockStmt {
                        span: DUMMY_SP,
                        stmts: vec![Stmt::Return(ReturnStmt {
                            span: DUMMY_SP,
                            arg: Some(Box::new(Expr::Ident(quote_ident!("error")))),
                        })],
                    })),
                    alt: None,
                });

                Stmt::Block(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![verify_call, error_check],
                })
            },
            _ => Stmt::Empty(EmptyStmt { span: DUMMY_SP }),
        }
    }

    fn check_oneof_field_type(
        &self,
        ctx: &mut Context,
        descriptor: &DescriptorProto,
        field: &FieldDescriptorProto,
    ) -> Stmt {
        let oneof_decl = &descriptor.oneof_decl[field.oneof_index() as usize];
        let oneof_container_name = normalize_name(oneof_decl.name());
        let oneof_choice_name = field.name().to_upper_camel_case();
        let inner_field_name = normalize_name(field.name());
        let choice_class_name = format!(
            "${}_{}", 
            oneof_container_name.to_upper_camel_case(),
            oneof_choice_name
        );

        // Create the typed variable name (same as choice name but camelCase)
        let typed_var_name = field.name().to_lower_camel_case();

        let typed_var_access = Box::new(Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!(&*typed_var_name))),
            prop: MemberProp::Ident(quote_ident!(&*inner_field_name)),
        }));

        let inner_check = self.build_scalar_check_for_expr(field, typed_var_access, &inner_field_name);

        Stmt::If(IfStmt {
            span: DUMMY_SP,
            test: Box::new(Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::LogicalAnd,
                left: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::LogicalAnd,
                    left: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEq,
                        left: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("message"))),
                            prop: MemberProp::Ident(quote_ident!(&*oneof_container_name)),
                        })),
                        right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
                    })),
                    right: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("message"))),
                            prop: MemberProp::Ident(quote_ident!("hasOwnProperty")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(Expr::Lit(Lit::Str(oneof_container_name.clone().into()))),
                        }],
                        type_args: None,
                    })),
                })),
                right: Box::new(Expr::Paren(ParenExpr {
                    span: DUMMY_SP,
                    expr: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::InstanceOf,
                        left: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("message"))),
                            prop: MemberProp::Ident(quote_ident!(&*oneof_container_name)),
                        })),
                        right: Box::new(Expr::Ident(quote_ident!(&*choice_class_name))),
                    })),
                })),
            })),
            cons: Box::new(Stmt::Block(BlockStmt {
                span: DUMMY_SP,
                stmts: {
                    // Determine if this is the first oneof field (index 0 in the oneof group)
                    let is_first_oneof_field = descriptor.field.iter()
                        .filter(|f| f.has_oneof_index() && f.oneof_index() == field.oneof_index())
                        .position(|f| f.name() == field.name())
                        .unwrap_or(0) == 0;

                    let mut stmts = vec![];

                    // For the first field, set properties = 1 first
                    if is_first_oneof_field {
                        stmts.push(Stmt::Expr(ExprStmt {
                            span: DUMMY_SP,
                            expr: Box::new(Expr::Assign(AssignExpr {
                                span: DUMMY_SP,
                                op: AssignOp::Assign,
                                left: AssignTarget::Simple(SimpleAssignTarget::Ident(BindingIdent {
                                    id: quote_ident!("properties"),
                                    type_ann: None,
                                })),
                                right: Box::new(Expr::Lit(Lit::Num(Number {
                                    span: DUMMY_SP,
                                    value: 1.0,
                                    raw: None,
                                }))),
                            })),
                        }));
                    } else {
                        // For non-first fields, add the multiple values check first
                        stmts.push(Stmt::If(IfStmt {
                            span: DUMMY_SP,
                            test: Box::new(Expr::Bin(BinExpr {
                                span: DUMMY_SP,
                                op: BinaryOp::EqEqEq,
                                left: Box::new(Expr::Ident(quote_ident!("properties"))),
                                right: Box::new(Expr::Lit(Lit::Num(Number {
                                    span: DUMMY_SP,
                                    value: 1.0,
                                    raw: None,
                                }))),
                            })),
                            cons: Box::new(Stmt::Block(BlockStmt {
                                span: DUMMY_SP,
                                stmts: vec![Stmt::Return(self.create_return_stmt(format!("{}: multiple values", oneof_container_name)))],
                            })),
                            alt: None,
                        }));

                        // Then set properties = 1
                        stmts.push(Stmt::Expr(ExprStmt {
                            span: DUMMY_SP,
                            expr: Box::new(Expr::Assign(AssignExpr {
                                span: DUMMY_SP,
                                op: AssignOp::Assign,
                                left: AssignTarget::Simple(SimpleAssignTarget::Ident(BindingIdent {
                                    id: quote_ident!("properties"),
                                    type_ann: None,
                                })),
                                right: Box::new(Expr::Lit(Lit::Num(Number {
                                    span: DUMMY_SP,
                                    value: 1.0,
                                    raw: None,
                                }))),
                            })),
                        }));
                    }

                    // Create typed variable with type annotation and cast
                    stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
                        span: DUMMY_SP,
                        kind: VarDeclKind::Let,
                        declare: false,
                        decls: vec![VarDeclarator {
                            span: DUMMY_SP,
                            name: Pat::Ident(BindingIdent {
                                id: quote_ident!(&*typed_var_name),
                                type_ann: Some(Box::new(TsTypeAnn {
                                    span: DUMMY_SP,
                                    type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                        span: DUMMY_SP,
                                        type_name: TsEntityName::Ident(quote_ident!(&*choice_class_name)),
                                        type_params: None,
                                    })),
                                })),
                            }),
                            init: Some(Box::new(Expr::TsAs(TsAsExpr {
                                span: DUMMY_SP,
                                expr: Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Ident(quote_ident!("message"))),
                                    prop: MemberProp::Ident(quote_ident!(&*oneof_container_name)),
                                })),
                                type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                    span: DUMMY_SP,
                                    type_name: TsEntityName::Ident(quote_ident!(&*choice_class_name)),
                                    type_params: None,
                                })),
                            }))),
                            definite: false,
                        }],
                    }))));

                    // Add the inner field validation
                    stmts.push(inner_check);

                    stmts
                },
            })),
            alt: None,
        })
    }

    fn create_return_stmt(&self, message: String) -> ReturnStmt {
        ReturnStmt {
            span: DUMMY_SP,
            arg: Some(Box::new(Expr::Lit(Lit::Str(Str {
                span: DUMMY_SP,
                value: message.into(),
                raw: None,
            })))),
        }
    }

    fn create_return_block(&self, message: String) -> Stmt {
        Stmt::Block(BlockStmt {
            span: DUMMY_SP,
            stmts: vec![Stmt::Return(self.create_return_stmt(message))],
        })
    }

    fn create_typeof_check(&self, arg: Box<Expr>, type_str: &str, message: String) -> IfStmt {
        IfStmt {
            span: DUMMY_SP,
            test: Box::new(Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::NotEqEq,
                left: Box::new(Expr::Unary(UnaryExpr {
                    span: DUMMY_SP,
                    op: UnaryOp::TypeOf,
                    arg,
                })),
                right: Box::new(Expr::Lit(Lit::Str(type_str.into()))),
            })),
            cons: Box::new(self.create_return_block(message)),
            alt: None,
        }
    }
}